package cn.akwangl.concurrency.web.model.order.service.v2.impl;

import cn.akwangl.concurrency.web.common.OrderConstant;
import cn.akwangl.concurrency.web.exception.BizException;
import cn.akwangl.concurrency.web.model.items.pojo.ItemsEntity;
import cn.akwangl.concurrency.web.model.items.pojo.ItemsImgEntity;
import cn.akwangl.concurrency.web.model.items.pojo.ItemsSpecEntity;
import cn.akwangl.concurrency.web.model.items.service.ItemsImgService;
import cn.akwangl.concurrency.web.model.items.service.ItemsService;
import cn.akwangl.concurrency.web.model.items.service.ItemsSpecService;
import cn.akwangl.concurrency.web.model.order.pojo.*;
import cn.akwangl.concurrency.web.model.order.service.OrderItemsService;
import cn.akwangl.concurrency.web.model.order.service.OrderStatusService;
import cn.akwangl.concurrency.web.model.order.service.OrdersService;
import cn.akwangl.concurrency.web.model.order.service.v2.CreateOrdersV2Service;
import cn.akwangl.concurrency.web.model.user.pojo.UserAddressEntity;
import cn.akwangl.concurrency.web.model.user.service.UserAddressService;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RScript;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;


/**
 * 创建订单 v2.0
 *
 * @author 阿坤同学
 * @date 2022/11/27 16:01
 */
@Slf4j
@Service("createOrdersV2Service")
public class CreateOrdersV2ServiceImpl implements CreateOrdersV2Service {

    private UserAddressService userAddressService;

    private ItemsSpecService itemsSpecService;

    private ItemsService itemsService;

    private ItemsImgService itemsImgService;

    private OrderItemsService orderItemsService;

    private OrderStatusService orderStatusService;

    private OrdersService ordersService;

    private PlatformTransactionManager platformTransactionManager;

    private TransactionDefinition transactionDefinition;

    private RedissonClient redissonClient;

    @Autowired
    public void setUserAddressService(UserAddressService userAddressService) {
        this.userAddressService = userAddressService;
    }

    @Autowired
    public void setItemsSpecService(ItemsSpecService itemsSpecService) {
        this.itemsSpecService = itemsSpecService;
    }

    @Autowired
    public void setItemsService(ItemsService itemsService) {
        this.itemsService = itemsService;
    }

    @Autowired
    public void setItemsImgService(ItemsImgService itemsImgService) {
        this.itemsImgService = itemsImgService;
    }

    @Autowired
    public void setOrderItemsService(OrderItemsService orderItemsService) {
        this.orderItemsService = orderItemsService;
    }

    @Autowired
    public void setOrderStatusService(OrderStatusService orderStatusService) {
        this.orderStatusService = orderStatusService;
    }

    @Autowired
    public void setOrdersService(OrdersService ordersService) {
        this.ordersService = ordersService;
    }

    @Autowired
    public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
        this.platformTransactionManager = platformTransactionManager;
    }

    @Autowired
    public void setTransactionDefinition(TransactionDefinition transactionDefinition) {
        this.transactionDefinition = transactionDefinition;
    }

    @Autowired
    public void setRedissonClient(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    private boolean verifyStock(String itemSpecId, Integer buyCounts) {
        String redisKey = OrderConstant.ITEMS_STOCK + itemSpecId;

        RScript script = redissonClient.getScript();
        String luaScript = "if redis.call('get', KEYS[1]) >= ARGV[1] then redis.call('decrby', KEYS[1], ARGV[1]) return 1 else return 0 end";
        List<Object> keys = Collections.singletonList(redisKey);
        return script.eval(RScript.Mode.READ_WRITE, luaScript, RScript.ReturnType.BOOLEAN, keys, buyCounts);
    }

    private boolean redisStockRollback(String itemSpecId, Integer buyCounts) {
        String redisKey = OrderConstant.ITEMS_STOCK + itemSpecId;

        RScript script = redissonClient.getScript();
        String luaScript = "return redis.call('incrby', KEYS[1], ARGV[1])";
        List<Object> keys = Collections.singletonList(redisKey);
        try {
            return script.eval(RScript.Mode.READ_WRITE, luaScript, RScript.ReturnType.BOOLEAN, keys, buyCounts);
        } catch (Exception e) {
            log.error("预扣减库存redis 数据回滚失败", e);
            log.info("redis 库存数据回滚失败 redisKey={},stock=+{}", redisKey, buyCounts);
            return false;
        }
    }


    @Override
    public OrderVo createOrder(SubmitOrderDto submitOrderDto) {
        List<ShopCart> shopCarts = submitOrderDto.getShopCarts();
        String addressId = submitOrderDto.getAddressId();

        List<String> itemIds = new ArrayList(shopCarts.size());
        List<String> itemSpecIds = new ArrayList(shopCarts.size());
        for (ShopCart shopCart : shopCarts) {
            String itemId = shopCart.getItemId();
            String itemSpecId = shopCart.getItemSpecId();
            Integer buyCounts = shopCart.getBuyCounts();

            // 校验库存
            if (!verifyStock(itemSpecId, buyCounts)) {
                throw new BizException("下单失败,部分商品库存不足");
            }
            itemIds.add(itemId);
            itemSpecIds.add(itemSpecId);
        }
        TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
        try {
            // 获取商品信息
            Map<String, ItemsEntity> itemsEntityMap = itemsService.getItemsEntityMap(itemIds);
            Map<String, ItemsImgEntity> itemsImgEntityMap = itemsImgService.getItemsImgEntityIsMainMap(itemIds);
            Map<String, ItemsSpecEntity> itemsSpecEntityMap = itemsSpecService.getItemsSpecEntityMap(itemSpecIds);

            // 订单Id
            String orderId = IdWorker.getIdStr();
            // 邮费
            int postAmount = 0;
            // 账单总价
            Integer totalAmount = 0;
            // 实际付款总价
            Integer realPayAmount = 0;
            List<OrderItemsEntity> orderItemsEntities = new ArrayList<>(shopCarts.size());
            for (ShopCart shopCart : shopCarts) {
                String itemsEntityMapKey = shopCart.getItemId();
                String itemsSpecEntityMapKey = shopCart.getItemSpecId();
                Integer buyCounts = shopCart.getBuyCounts();
                ItemsEntity itemsEntity = itemsEntityMap.get(itemsEntityMapKey);
                ItemsSpecEntity itemsSpecEntity = itemsSpecEntityMap.get(itemsSpecEntityMapKey);
                ItemsImgEntity itemsImgEntity = itemsImgEntityMap.get(itemsEntityMapKey);

                OrderItemsEntity orderItems = OrderFactory.createOrderItemsEntity(buyCounts, orderId, itemsEntity, itemsSpecEntity, itemsImgEntity);
                orderItemsEntities.add(orderItems);
                // 统计订单总价
                totalAmount += itemsSpecEntity.getPriceNormal() * buyCounts;
                realPayAmount += itemsSpecEntity.getPriceDiscount() * buyCounts;
            }
            // 保存订单明细
            orderItemsService.saveBatch(orderItemsEntities);
            // 扣减库存
            boolean decreaseStockFlag = itemsSpecService.decreaseItemSpecStock(shopCarts);
            if (!decreaseStockFlag) {
                throw new BizException("库存扣减失败");
            }

//            if (Thread.currentThread().getId() % 2 == 0) {
//                throw new RuntimeException("测试异常数据回滚");
//            }

            // 获取用户地址
            UserAddressEntity userAddressEntity = userAddressService.getById(addressId);
            // 创建订单
            OrdersEntity ordersEntity = OrderFactory.createOrderEntity(orderId, postAmount, totalAmount, realPayAmount, submitOrderDto, userAddressEntity);
            ordersService.save(ordersEntity);
            // 创建订单状态
            OrderStatusEntity orderStatusEntity = OrderFactory.createOrderStatusEntity(ordersEntity);
            orderStatusService.save(orderStatusEntity);
            // 创建商户信息
            MerchantOrdersVo merchantOrdersVo = OrderFactory.createMerchantOrdersVo(ordersEntity);
            // 创建订单VO
            OrderVo orderVo = OrderFactory.createOrderVo(orderId, shopCarts, merchantOrdersVo);
            platformTransactionManager.commit(transaction);
            return orderVo;
        } catch (Exception e) {
            platformTransactionManager.rollback(transaction);

            // redis 库存回滚
            for (ShopCart shopCart : shopCarts) {
                String itemSpecId = shopCart.getItemSpecId();
                Integer buyCounts = shopCart.getBuyCounts();
                redisStockRollback(itemSpecId, buyCounts);

            }
            return null;
        }
    }
}
